D:\git\skunkworks\herald-for-cpp\herald\include\herald\datatype\randomness.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2020-2021 Herald Project Contributors |
2 | | // SPDX-License-Identifier: Apache-2.0 |
3 | | // |
4 | | |
5 | | #ifndef HERALD_RANDOMNESS_H |
6 | | #define HERALD_RANDOMNESS_H |
7 | | |
8 | | #include "data.h" |
9 | | |
10 | | #include <string> |
11 | | #include <random> |
12 | | #include <climits> |
13 | | |
14 | | namespace herald { |
15 | | namespace datatype { |
16 | | |
17 | | /** |
18 | | * A Randomness Source provides random data. It could be a simple |
19 | | * uniform integer distribution, it could be a predictable sequence, |
20 | | * it could be derived from a chipsets secure enclave, if present. |
21 | | * |
22 | | * This high level class abstracts away the underlying implementation |
23 | | * mechanics. On native platforms in C++ the implementations available may |
24 | | * vary dramatically. |
25 | | */ |
26 | | // class RandomnessSource { |
27 | | // public: |
28 | | // RandomnessSource() = default; |
29 | | // virtual ~RandomnessSource() = default; |
30 | | |
31 | | // virtual std::string methodName() const = 0; |
32 | | |
33 | | // virtual void nextBytes(std::size_t count, Data& into) = 0; |
34 | | // virtual int nextInt() = 0; |
35 | | // virtual double nextDouble() = 0; |
36 | | |
37 | | // }; |
38 | | |
39 | | /** |
40 | | * A decidedly non random source!!! Used to test the v4 UUID format generation function only. |
41 | | * DO NOT USE IN PRODUCTION. |
42 | | */ |
43 | | class AllZerosNotRandom { |
44 | | public: |
45 | | AllZerosNotRandom() = default; |
46 | | AllZerosNotRandom(AllZerosNotRandom&& other) noexcept = default; |
47 | | ~AllZerosNotRandom() = default; |
48 | | |
49 | 0 | std::string methodName() const { |
50 | 0 | return "allzeros"; |
51 | 0 | } |
52 | | |
53 | 2 | void nextBytes(std::size_t count, Data& into) { |
54 | 22 | for (std::size_t i = 0;i < count;i++20 ) { |
55 | 20 | into.append(std::byte(0)); |
56 | 20 | } |
57 | 2 | } |
58 | | |
59 | 1 | int nextInt() { |
60 | 1 | return 0; |
61 | 1 | } |
62 | | |
63 | 1 | double nextDouble() { |
64 | 1 | return 0.0; |
65 | 1 | } |
66 | | }; |
67 | | |
68 | | class IntegerDistributedRandomSource { |
69 | | public: |
70 | | IntegerDistributedRandomSource() |
71 | | : rd(), gen(rd()), distrib(LONG_MIN,LONG_MAX) |
72 | 3 | { |
73 | 3 | ; |
74 | 3 | } |
75 | | |
76 | | IntegerDistributedRandomSource(IntegerDistributedRandomSource&& other) noexcept |
77 | | : rd(), // Doesn't have a move or copy constructor |
78 | | gen(rd()), |
79 | | distrib(other.distrib) |
80 | 3 | { |
81 | 3 | ; |
82 | 3 | } |
83 | | |
84 | | ~IntegerDistributedRandomSource() = default; |
85 | | |
86 | 0 | std::string methodName() const { |
87 | 0 | return "integerdistributed"; |
88 | 0 | } |
89 | | |
90 | 4 | void nextBytes(std::size_t count, Data& into) { |
91 | 68 | for (std::size_t i = 0;i < count;i++64 ) { |
92 | 64 | into.append(std::byte(distrib(gen))); |
93 | 64 | } |
94 | 4 | } |
95 | | |
96 | 0 | int nextInt() { |
97 | 0 | return (int)distrib(gen); |
98 | 0 | } |
99 | | |
100 | 0 | double nextDouble() { |
101 | 0 | return (double)distrib(gen); |
102 | 0 | } |
103 | | |
104 | | private: |
105 | | std::random_device rd; // Will be used to obtain a seed for the random number engine |
106 | | std::mt19937 gen; // Standard mersenne_twister_engine seeded with rd() |
107 | | std::uniform_int_distribution<int64_t> distrib; |
108 | | }; |
109 | | |
110 | | /** |
111 | | * The Randomness Generator IS A source of randomness, but may also |
112 | | * be used by the application to inject a secondary source of entropy |
113 | | * in to the result of the underlying RandomnessSource implementation. |
114 | | * |
115 | | * This is the class used by Herald. This allows application developers |
116 | | * to use the most appropriate randomness source for their target |
117 | | * platform and application needs. |
118 | | * |
119 | | * A secondary source of entropy may be something else going on in the |
120 | | * app. In Herald, for example, it could be the time it actually takes |
121 | | * to complete a scan-and-interact loop. This combines outside effects |
122 | | * of communication, internal timer/interrupt timing changes, and |
123 | | * internal state computation times to make the entropy unpredictable. |
124 | | */ |
125 | | template <typename RandomnessSourceT> |
126 | | class RandomnessGenerator { |
127 | | public: |
128 | | RandomnessGenerator(RandomnessSourceT&& toOwn) |
129 | | : m_source(std::move(toOwn)), |
130 | | m_entropy(0) |
131 | 4 | { |
132 | 4 | ; |
133 | 4 | } ??0?$RandomnessGenerator@VAllZerosNotRandom@datatype@herald@@@datatype@herald@@QEAA@$$QEAVAllZerosNotRandom@12@@Z Line | Count | Source | 131 | 1 | { | 132 | 1 | ; | 133 | 1 | } |
??0?$RandomnessGenerator@VIntegerDistributedRandomSource@datatype@herald@@@datatype@herald@@QEAA@$$QEAVIntegerDistributedRandomSource@12@@Z Line | Count | Source | 131 | 3 | { | 132 | 3 | ; | 133 | 3 | } |
|
134 | | |
135 | | ~RandomnessGenerator() = default; |
136 | | |
137 | | template <typename T> |
138 | | void addEntropy(T entropy) { |
139 | | // Get size of amount of entropy we have |
140 | | constexpr std::size_t size = sizeof(T); |
141 | | // TODO consider whether there's benefit to detecting most significant set bits. |
142 | | // This may provide a benefit if the method of combination isn't XOR. |
143 | | |
144 | | // See if we are at multiples of std::size_t |
145 | | constexpr std::size_t multiple = sizeof(std::size_t) / size; // integer division, rounds down |
146 | | // Note the above cannot ever be 0 because std::size_t is always the largest size on a given architecture |
147 | | |
148 | | std::size_t toXor = 0; |
149 | | for (std::size_t i = 0;i < multiple;i++) { |
150 | | if (0 != i) { |
151 | | toXor << size; |
152 | | } |
153 | | toXor += entropy; |
154 | | } |
155 | | |
156 | | // XOR will ensure the same distribution of 0 and 1 over multiple applications |
157 | | m_entropy = m_entropy ^ toXor; |
158 | | } |
159 | | |
160 | | |
161 | | std::string methodName() const { |
162 | | return m_source.methodName(); |
163 | | } |
164 | | |
165 | 5 | void nextBytes(std::size_t count, Data& into) { |
166 | 5 | constexpr std::size_t byteSize = sizeof(std::byte); |
167 | 5 | constexpr std::size_t sizeTSize = sizeof(std::size_t); |
168 | 5 | constexpr std::size_t shifts = sizeTSize / byteSize; |
169 | 5 | Data sourcedInto; |
170 | 5 | m_source.nextBytes(count,sourcedInto); |
171 | 5 | |
172 | 5 | // now add in entropy |
173 | 85 | for (std::size_t byteIndex = 0;byteIndex < count;byteIndex++80 ) { |
174 | 80 | into.append((std::byte)( |
175 | 80 | std::size_t(sourcedInto.at(byteIndex)) |
176 | 80 | ^ |
177 | 80 | (m_entropy >> 8 * (byteIndex % shifts)) |
178 | 80 | )); |
179 | 80 | } |
180 | 5 | } ?nextBytes@?$RandomnessGenerator@VAllZerosNotRandom@datatype@herald@@@datatype@herald@@QEAAX_KAEAV?$DataRef@V?$MemoryArena@$0CAAA@$07@datatype@herald@@@23@@Z Line | Count | Source | 165 | 1 | void nextBytes(std::size_t count, Data& into) { | 166 | 1 | constexpr std::size_t byteSize = sizeof(std::byte); | 167 | 1 | constexpr std::size_t sizeTSize = sizeof(std::size_t); | 168 | 1 | constexpr std::size_t shifts = sizeTSize / byteSize; | 169 | 1 | Data sourcedInto; | 170 | 1 | m_source.nextBytes(count,sourcedInto); | 171 | 1 | | 172 | 1 | // now add in entropy | 173 | 17 | for (std::size_t byteIndex = 0;byteIndex < count;byteIndex++16 ) { | 174 | 16 | into.append((std::byte)( | 175 | 16 | std::size_t(sourcedInto.at(byteIndex)) | 176 | 16 | ^ | 177 | 16 | (m_entropy >> 8 * (byteIndex % shifts)) | 178 | 16 | )); | 179 | 16 | } | 180 | 1 | } |
?nextBytes@?$RandomnessGenerator@VIntegerDistributedRandomSource@datatype@herald@@@datatype@herald@@QEAAX_KAEAV?$DataRef@V?$MemoryArena@$0CAAA@$07@datatype@herald@@@23@@Z Line | Count | Source | 165 | 4 | void nextBytes(std::size_t count, Data& into) { | 166 | 4 | constexpr std::size_t byteSize = sizeof(std::byte); | 167 | 4 | constexpr std::size_t sizeTSize = sizeof(std::size_t); | 168 | 4 | constexpr std::size_t shifts = sizeTSize / byteSize; | 169 | 4 | Data sourcedInto; | 170 | 4 | m_source.nextBytes(count,sourcedInto); | 171 | 4 | | 172 | 4 | // now add in entropy | 173 | 68 | for (std::size_t byteIndex = 0;byteIndex < count;byteIndex++64 ) { | 174 | 64 | into.append((std::byte)( | 175 | 64 | std::size_t(sourcedInto.at(byteIndex)) | 176 | 64 | ^ | 177 | 64 | (m_entropy >> 8 * (byteIndex % shifts)) | 178 | 64 | )); | 179 | 64 | } | 180 | 4 | } |
|
181 | | |
182 | | int nextInt() { |
183 | | return (int)(m_source.nextInt() ^ m_entropy); |
184 | | } |
185 | | |
186 | | double nextDouble() { |
187 | | return (double)(((std::size_t)m_source.nextDouble()) ^ m_entropy); |
188 | | } |
189 | | |
190 | | private: |
191 | | RandomnessSourceT m_source; |
192 | | std::size_t m_entropy; |
193 | | }; |
194 | | |
195 | | } // end namespace |
196 | | } // end namespace |
197 | | |
198 | | #endif |